package com.egor.views.components;

import com.egor.data.entity.Order;
import com.egor.data.enums.OrderStatus;
import com.egor.services.OrderService;
import com.egor.views.components.modalboxes.EditOrderModalBox;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.grid.dnd.GridDragEndEvent;
import com.vaadin.flow.component.grid.dnd.GridDragStartEvent;
import com.vaadin.flow.component.grid.dnd.GridDropMode;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.spring.annotation.RouteScope;
import com.vaadin.flow.spring.data.VaadinSpringDataHelpers;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Component;

@Component
@RouteScope
public class OrderComponent extends VerticalLayout {
    private final HorizontalLayout toolbar = new HorizontalLayout();
    private final HorizontalLayout grids = new HorizontalLayout();
    private final Grid<Order> grid1 = new Grid<>(Order.class);
    private final Grid<Order> grid2 = new Grid<>(Order.class);
    private final TextField filterText = new TextField();
    private final Button addButton = new Button("Add order");
    private final Button changeButton = new Button("Change order (at uncompleted)");
    private final Button deleteButton = new Button("Delete order (at uncompleted)");

    private final OrderService orderService;
    private final EditOrderModalBox editOrderModalBox;

    private Order draggedItem;

    public OrderComponent(OrderService orderService,
                          EditOrderModalBox editOrderModalBox) {
        this.orderService = orderService;
        this.editOrderModalBox = editOrderModalBox;

        addClassName("order-view");
        setSizeFull();

        setToolbar();
        setModalBox();
        setGrid();

        add(toolbar, grids);
    }

    private void setToolbar() {
        setButtons();
        setFilterText();

        toolbar.setWidthFull();
        toolbar.add(addButton, changeButton,
                deleteButton, filterText);
    }

    private void setGrid() {
        grid1.setColumns("name", "address", "dateOfOrder");
        grid2.setColumns("name", "address", "dateOfOrder");

        grid1.setItems(query -> orderService.findByStatus(OrderStatus.NOT_COMPLETED,
                        PageRequest.of(query.getPage(), query.getPageSize(), VaadinSpringDataHelpers.toSpringDataSort(query)))
                .stream());

        grid2.setItems(query -> orderService.findByStatus(OrderStatus.COMPLETED,
                        PageRequest.of(query.getPage(), query.getPageSize(), VaadinSpringDataHelpers.toSpringDataSort(query)))
                .stream());

        dragSetup();

        grid1.asSingleSelect().addValueChangeListener(event ->
                editOrderModalBox.setOrder(event.getValue()));

        VerticalLayout gridWithHeading1 = new VerticalLayout(new H2("Uncompleted"), grid1);
        VerticalLayout gridWithHeading2 = new VerticalLayout(new H2("Completed"), grid2);

        gridWithHeading1.setSizeFull();
        gridWithHeading2.setSizeFull();

        grids.setSizeFull();
        grids.add(gridWithHeading1, gridWithHeading2);
    }

    private void dragSetup() {
        grid1.setDropMode(GridDropMode.ON_GRID);
        grid1.setRowsDraggable(true);
        grid1.addDragStartListener(this::handleDragStart);
        grid1.addDropListener(e -> {
            draggedItem.setOrderStatus(OrderStatus.NOT_COMPLETED);
            orderService.save(draggedItem);
            updateList();
        });
        grid1.addDragEndListener(this::handleDragEnd);

        grid2.setDropMode(GridDropMode.ON_GRID);
        grid2.setRowsDraggable(true);
        grid2.addDragStartListener(this::handleDragStart);
        grid2.addDropListener(e -> {
            draggedItem.setOrderStatus(OrderStatus.COMPLETED);
            orderService.save(draggedItem);
            updateList();
        });
        grid2.addDragEndListener(this::handleDragEnd);
    }

    private void setButtons() {
        addButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);

        addButton.addClickListener(event -> addOrder());
        changeButton.addClickListener(event -> editOrder());
        deleteButton.addClickListener(event -> deleteOrder());
    }

    private void setFilterText() {
        filterText.setPlaceholder("Search...");
        filterText.setClearButtonVisible(true);
        filterText.setValueChangeMode(ValueChangeMode.LAZY);
        filterText.addValueChangeListener(e -> updateList());
    }

    private void setModalBox() {
        editOrderModalBox.addListener(EditOrderModalBox.SaveEvent.class, this::saveOrder);
        editOrderModalBox.addListener(EditOrderModalBox.CloseEvent.class, e -> closeEditor());
    }

    private void saveOrder(EditOrderModalBox.SaveEvent event) {
        orderService.save(event.getOrder());
        updateList();
        closeEditor();
    }

    public void editOrder() {
        editOrderModalBox.open();
    }

    private void deleteOrder() {
        Order order = grid1.asSingleSelect().getValue();

        if (order != null) {
            orderService.delete(order);
            updateList();
        }
    }

    private void addOrder() {
        grid1.asSingleSelect().clear();
        grid2.asSingleSelect().clear();

        editOrderModalBox.setOrder(new Order());
        editOrderModalBox.open();
    }

    private void closeEditor() {
        editOrderModalBox.setOrder(null);
        editOrderModalBox.close();
    }

    private void handleDragStart(GridDragStartEvent<Order> e) {
        draggedItem = e.getDraggedItems().get(0);
    }

    private void handleDragEnd(GridDragEndEvent<Order> e) {
        draggedItem = null;
    }

    private void updateList() {
        grid1.setItems(query -> orderService.findAll(filterText.getValue(), OrderStatus.NOT_COMPLETED,
                        PageRequest.of(query.getPage(), query.getPageSize(), VaadinSpringDataHelpers.toSpringDataSort(query)))
                .stream());

        grid2.setItems(query -> orderService.findAll(filterText.getValue(), OrderStatus.COMPLETED,
                        PageRequest.of(query.getPage(), query.getPageSize(), VaadinSpringDataHelpers.toSpringDataSort(query)))
                .stream());
    }
}
